package com.sq.partner.manager.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DBUtil {
	 public static final Class BigDecimalClass = BigDecimal.class;
	    public static final Class BlobClass = Blob.class;
	    public static final Class BooleanClass = Boolean.class;
	    public static final Class booleanClass = boolean.class;
	    public static final Class ByteClass = Byte.class;
	    public static final Class byteClass = byte.class;
	    public static final Class ClobClass = Clob.class;
	    public static final Class DateClass = Date.class;
	    public static final Class SqlDateClass = java.sql.Date.class;
	    public static final Class DoubleClass = Double.class;
	    public static final Class doubleClass = double.class;
	    public static final Class FloatClass = Float.class;
	    public static final Class floatClass = float.class;
	    public static final Class IntegerClass = Integer.class;
	    public static final Class intClass = int.class;
	    public static final Class LongClass = Long.class;
	    public static final Class longClass = long.class;
	    public static final Class ObjectClass = Object.class;
	    public static final Class ShortClass = Short.class;
	    public static final Class shortClass = short.class;
	    public static final Class stringClass = String.class;
	    public static final Class TimestampClass = Timestamp.class;
	/**
	 * 将resultSet结果放到T里，结果不区分大小写
	 * @param rs
	 * @return T instance 
	 * @throws SQLException
	 */
	public static <T> T transRs2Bean(Class<T> clz,ResultSet rs) throws Exception {
		List<T> list = transRs2List(clz,rs);
		if(list == null){
			return null;
		}
		
		if(list.isEmpty()){
			return null;
		}else if(list.size() > 1){
			throw new SQLException("rs记录超过一条");
		}
		
		return list.get(0);
    }
	
	/**
	 * 将resultSet结果放到List<T>里
	 * 调用bean的公共set方法赋值
	 * 可以匹配 驼峰标志和带下划线的格式(带下划线的全部转为大写)
	 * 如 setBeanName 会去rs中取beanName和BEAN_NAME值
	 * setBEAN_NAME 会去 rs中取 BEAN_NAME和 beanName值
	 * @param rs
	 * @return List<T> ，没有结果返回null 
	 * @throws SQLException
	 */
	public static <T> List<T> transRs2List(Class<T> clz,ResultSet rs) throws Exception {
		List<T> list = null;
        if (rs.next()){
        	list = new ArrayList<T>();
        	ResultSetMetaData resultMeta = rs.getMetaData();
        	List<String> nameList = new ArrayList<String>();
            for (int j = 1; j <= resultMeta.getColumnCount(); j++) {
            	nameList.add(resultMeta.getColumnName(j).toUpperCase());
            }
        	
            do{
            	T obj = clz.newInstance();
        		Method[] ms = clz.getMethods();//获取所有公共方法
        		for(Method m : ms){
        			String name = m.getName();
        			if(name.startsWith("set")){
        				String prop = name.replaceFirst("set","");
        				if(prop.indexOf("_") == -1){
        					prop = prop.substring(0,1).toLowerCase()+prop.substring(1);
        				}else{
        					prop = prop.toUpperCase();
        				}
        				
        				Class[] paraList = m.getParameterTypes();
        				if(paraList.length != 1){
        					throw new Exception(clz.getName()+"."+name+"参数为空或没有参数，请检查bean结构");
        				}
        				Class paraClz = paraList[0];
        				Object val = null;
        				if(nameList.contains(prop.toUpperCase())){//原始属性
        					val = getValue(paraClz,rs,prop);
        				}else{//原始属性无值，则转成另一种属性
        					prop = (transStr(prop));
        					if(nameList.contains(prop.toUpperCase())){//原始属性
            					val = getValue(paraClz,rs,prop);
            				}
        				}
        				
        				if(val != null){
        					m.invoke(obj,new Object[]{val});
        				}
        			}
        		}
            	
        		list.add(obj);
            }while(rs.next());
        }
		
		return list;
    }
	
	/**
	 * 将resultSet结果放到List<Map>里，结果不区分大小写
	 * @param rs rs不要调用rs.next
	 * @return List<Map> ，没有结果返回null 
	 * @throws SQLException
	 */
	public static List<Map> transRs2List(ResultSet rs) throws SQLException {
        List re = null;
        try {
            ResultSetMetaData resultMeta = rs.getMetaData();
            DBMap to = null;
            if (rs.next()){
                re = new ArrayList();
                do{
                    to = new DBMap();
                    for (int j = 1; j <= resultMeta.getColumnCount(); j++) {
                    	to.put( resultMeta.getColumnName(j),rs.getObject(j));
                    }
                    re.add(to);
                }while(rs.next());
            }
        } catch (Exception e) {
            throw new SQLException("Load to DBMap failed");
        }
        return re;
    }
	
	/**
	 * 调用公共set方法将map的值放到bean里面
	 * 可以匹配 驼峰标志和带下划线的格式(带下划线的全部转为大写)
	 * 如 setBeanName 会去map中取beanName和BEAN_NAME值
	 * setBEAN_NAME 会去 map中取 BEAN_NAME和 beanName值
	 * 建议Map 使用DBMap
	 * @param <T>
	 * @param clz
	 * @param map
	 * @return T instance
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 * @throws InvocationTargetException 
	 * @throws IllegalArgumentException 
	 * @throws Exception
	 */
	public static <T>   T transMap2Bean(Class<T> clz,Map map) throws Exception {
		T obj = clz.newInstance();
		Method[] ms = clz.getMethods();//获取所有公共方法
		for(Method m : ms){
			String name = m.getName();
			if(name.startsWith("set")){
				String prop = name.replaceFirst("set","");
				if(prop.indexOf("_") == -1){
					prop = prop.substring(0,1).toLowerCase()+prop.substring(1);
				}else{
					prop = prop.toUpperCase();
				}
				
				Object val = map.get(prop);
				if(val == null){
					val = map.get(transStr(prop));
				}
				
				if(val != null){
					Class paraClaz = m.getParameterTypes()[0];
					Object val2 = getValue(paraClaz, val);
					m.invoke(obj,new Object[]{val2});
				}
			}
		}
		
		return obj;
	}
	
	/**
	 * 调用公共set方法将map的值放到bean里面
	 * 可以匹配 驼峰标志和带下划线的格式(带下划线的全部转为大写)
	 * 如 setBeanName 会去map中取beanName和BEAN_NAME值
	 * setBEAN_NAME 会去 map中取 BEAN_NAME和 beanName值
	 * 建议Map 使用DBMap
	 * @param <T>
	 * @param clz
	 * @param mapList
	 * @return  List<T> instance
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static <T>   List<T> transMap2BeanList(Class<T> clz,List<Map> mapList) throws Exception {
		List<T> objList = new ArrayList<T>();
		for(Map map : mapList) {
			objList.add(transMap2Bean(clz, map));
		}
		return objList;
	}
	
	private static Object getValue(Class clz, Object val) throws Exception{
		if(val==null){return null;}
		if (SqlDateClass.equals(clz) || BigDecimalClass.equals(clz) || ClobClass.equals(clz) ||  ObjectClass.equals(clz)){
            return val;
        }else if (stringClass.equals(clz)){
            if(val instanceof Clob){
                Clob c = (Clob)val;
                //GBK
                StringBuffer sb = new StringBuffer();
                long length = c.length();
                int arrayLength = 612;
                //初使化每次读取长度
                int l = length > arrayLength?arrayLength:(int)length;
                Reader read = c.getCharacterStream();
                char[] cc = new char[l];
                while (read.read(cc) != -1){
                    sb.append(new String(cc));
                    length -= arrayLength;
                    //最后一次不要多读
                    if(length < arrayLength && length > 0){
                        cc = new char[(int)length];
                    }
                }
                read.close();
                return  sb.toString();
            }
            return val.toString();
        }else if (LongClass.equals(clz) || longClass.equals(clz)){
            return ((BigDecimal)val).longValue();
        }else if (DoubleClass.equals(clz) || doubleClass.equals(clz)){
            return ((BigDecimal)val).doubleValue();
        }else if (DateClass.equals(clz)){
            return new Date(((java.sql.Date)val).getTime());
        }else if (Timestamp.class.equals(clz)){
            if(val instanceof Timestamp){
                return val;
            }else{
                return new Timestamp(((java.sql.Date)val).getTime());
            }
        }else if (IntegerClass.equals(clz) || intClass.equals(clz)){
            return ((BigDecimal)val).intValue();
        }else if (BlobClass.equals(clz)){
            return val;
        }else if (BooleanClass.equals(clz) || booleanClass.equals(clz)){
            //暂不支持
            throw new IllegalArgumentException("未处理的数据类型："+clz.getName()+"请在SQLUtil中添加");
        }else if (ByteClass.equals(clz) || byteClass.equals(clz)){
            //暂不支持
            throw new IllegalArgumentException("未处理的数据类型："+clz.getName()+"请在SQLUtil中添加");
        }else if (FloatClass.equals(clz) || floatClass.equals(clz)){
            return ((BigDecimal)val).floatValue();
        }else if (ShortClass.equals(clz) || shortClass.equals(clz)){
            //暂不支持
            throw new IllegalArgumentException("未处理的数据类型："+clz.getName()+"请在SQLUtil中添加");
            //return ((BigDecimal)val).shortValue();
        }else{
            throw new IllegalArgumentException("未处理的数据类型："+clz.getName()+"请在SQLUtil中添加");
        }
	}
	
	private static Object getValue(Class clz, ResultSet rs,String name) throws Exception{
		if (stringClass.equals(clz)){
			return rs.getString(name);
		}else if (LongClass.equals(clz) || longClass.equals(clz)){
			return rs.getLong(name);
		}else if (BigDecimalClass.equals(clz)){
			return rs.getBigDecimal(name);
		}else if (DoubleClass.equals(clz) || doubleClass.equals(clz)){
			return rs.getDouble(name);
		}else if (DateClass.equals(clz)){
			return rs.getDate(name);
		}else if (TimestampClass.equals(clz)){
			return rs.getTimestamp(name);
		}else if (IntegerClass.equals(clz) || intClass.equals(clz)){
			return rs.getInt(name);
		}else if (BlobClass.equals(clz)){
			return rs.getBlob(name);
		}else if (BooleanClass.equals(clz) || booleanClass.equals(clz)){
			return rs.getBoolean(name);
		}else if (ByteClass.equals(clz) || byteClass.equals(clz)){
			return rs.getByte(name);
		}else if (ClobClass.equals(clz)){
			return rs.getClob(name);
		
		}else if (FloatClass.equals(clz) || floatClass.equals(clz)){
			return rs.getFloat(name);
		}else if (ShortClass.equals(clz) || shortClass.equals(clz)){
			return rs.getShort(name);
		}else if (ObjectClass.equals(clz)){
			return rs.getObject(name);
		}else{
			throw new IllegalArgumentException("未处理的数据类型："+clz.getName()+"请在DBUtil中添加");
		}
	}
	
	/**
	 * 将带下划线的转成驼峰标志，如果是驼峰标志的，则转成全部大写的下划线标志
	 * @param s
	 * @return
	 */
	public  final static String transStr(String s){
		if(s == null || "".equals(s)){
			return s;
		}
		
		if(s.indexOf("_") == -1){
			if(s.matches("^[^A-Z]+$")){
				return s;
			}else{
				s = s.substring(0,1) + s.substring(1).replaceAll("[A-Z]","_$0");
				s = s.toUpperCase();
				return s;
			}
			
		}else{
			String resut = "";
			String[] sArray = s.toLowerCase().split("_");
			for(int i = 0; i < sArray.length;i++){
				String st = sArray[i];
				if(i == 0){
					resut += st;
				}else{
					resut += st.substring(0,1).toUpperCase() + st.substring(1);
				}
			}
			return resut;
		}
		
	}
	/** 
     * 将一个 JavaBean 对象转化为一个  Map 
     * @param bean 要转化的JavaBean 对象 
     * @return 转化出来的  Map 对象 
     * @throws IntrospectionException 如果分析类属性失败 
     * @throws IllegalAccessException 如果实例化 JavaBean 失败 
     * @throws InvocationTargetException 如果调用属性的 setter 方法失败 
     */  
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static Map convertBean(Object bean)
            throws IntrospectionException, IllegalAccessException, InvocationTargetException {
        Class type = bean.getClass();
        Map returnMap = new HashMap();
        BeanInfo beanInfo = Introspector.getBeanInfo(type);
        PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors();
        for (int i = 0; i< propertyDescriptors.length; i++) {
            PropertyDescriptor descriptor = propertyDescriptors[i];
            String propertyName = descriptor.getName();
            if (!propertyName.equals("class")) {
                Method readMethod = descriptor.getReadMethod();
                Object result = readMethod.invoke(bean, new Object[0]);
                if (result != null) {
                    returnMap.put(propertyName, result);
                } else {
                    returnMap.put(propertyName, "");
                }
            }
        }
        return returnMap;
    }  
}
